{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2 as cv\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 轮廓 \n",
    "\n",
    "## 什么是轮廓\n",
    "\n",
    "轮廓可以简单地解释为连接所有具有相同的颜色或强度的连续点（沿着边界）的曲线。轮廓是形状分析和物体检测和识别的很有用的工具。\n",
    "\n",
    "* 为了更好的准确性，使用二进制图像，因此，在找到轮廓之前，应用阈值或canny边缘检测。\n",
    "* 从OpenCV 3.2开始，findContours()不再修改源图像，而是将修改后的图像作为三个返回参数中的第一个返回。\n",
    "* 在OpenCV中，找到轮廓就像从黑色背景中找到白色物体。所以请记住，要找到的对象应该是白色，背景应该是黑色。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "img = cv.imread(\"img/mh/3-1.png\")\n",
    "imggray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)\n",
    "ret, thresh = cv.threshold(imggray, 127, 255, 0)\n",
    "# 在cv.findContours()函数中有三个参数，\n",
    "#   第一个是源图像，\n",
    "#   第二个是轮廓检索模式，\n",
    "#   第三个是轮廓逼近方法。它输出轮廓和层次结构。\n",
    "# contours是图像中所有轮廓的Python列表，每个单独的轮廓是对象边界点坐标(x,y)的Numpy数组。\n",
    "contours, hierarchy = cv.findContours(\n",
    "    thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 如何绘制轮廓？\n",
    "要绘制轮廓，可以使用cv.drawContours函数。如果图像有边界点，它也可以用于绘制任何形状。它的第一个参数是源图像，第二个参数是应该作为Python列表传递的轮廓，第三个参数是轮廓索引（在绘制单个轮廓时很有用。绘制所有轮廓，传递-1），其余参数是颜色，厚度等等\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 要绘制图像中的所有轮廓：\n",
    "# img = cv.drawContours(imggray, contours, -1, (0, 255, 0), 3)\n",
    "# 要绘制单个轮廓，请输入四个轮廓点：\n",
    "# img = cv.drawContours(img, contours, 3, (0, 255, 0), 3)\n",
    "# 但大多数时候，下面的方法会很有用：\n",
    "cnt = contours[4]\n",
    "img = cv.drawContours(imggray, [cnt], 0, (0, 255, 0), 3)\n",
    "\n",
    "## 注意：最后两种方法是相同的，但是当你继续前进时，你会发现最后一种方法更有用。\n",
    "\n",
    "cv.imshow(\"1\", img)\n",
    "cv.imshow(\"2\", thresh)\n",
    "cv.waitKey(0)\n",
    "cv.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 轮廓近似方法\n",
    "这是cv.findContours函数中的第三个参数。它实际上表示什么？\n",
    "\n",
    "在上面，我们告诉轮廓是具有相同强度的形状的边界。它存储形状边界的（x，y）坐标。但是它存储了所有坐标吗？这由该轮廓近似方法指定。\n",
    "\n",
    "如果传递cv.CHAIN_APPROX_NONE，则存储所有边界点。但实际上我们需要所有的积分吗？例如，你找到了直线的轮廓，你是否需要线上的所有点来表示该线？不，我们只需要该线的两个端点。这就是cv.CHAIN_APPROX_SIMPLE的作用。它删除所有冗余点并压缩轮廓，从而节省内存。\n",
    "\n",
    "下面的矩形图像展示了这种技术。只需在轮廓阵列中的所有坐标上绘制一个圆圈（以蓝色绘制）。第一张图片显示了我用cv.CHAIN_APPROX_NONE（734点）获得的点数，第二张图片显示了一张带有cv.CHAIN_APPROX_SIMPLE（仅4点）的点数，它节省了不少内存！"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "8b820dafb18f42d6e5f840dc2f0c20b46468de59ce39267dd760d8325dfbd3ca"
  },
  "kernelspec": {
   "display_name": "Python 3.9.7 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
